﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;

namespace StableDiffusionTools.Utils
{
    public class RequestResult<T>
    {
        public T Data { get; private set; }
        public string Error { get; private set; } = "";
        public bool Success { get; private set; }
        public RequestResult(T responseData, string error, bool success)
        {
            Data = responseData;
            Error = error;
            Success = success;
        }
    }

    public static class HttpClientUtil
    {
        private static readonly HttpClient HttpClient;
        private static readonly JsonSerializerOptions jsonSerializerOptions;
        static HttpClientUtil()
        {
            HttpClient = new HttpClient();
            //HttpClient.DefaultRequestHeaders.ConnectionClose = true;

            //HttpClient.DefaultRequestHeaders.Add("Connection", "keep-alive");
            //HttpClient.DefaultRequestHeaders.Add("Keep-Alive", "5");

            jsonSerializerOptions = new JsonSerializerOptions
            {
                //PropertyNameCaseInsensitive = true,
                NumberHandling = JsonNumberHandling.AllowReadingFromString
            };
        }

        public static async Task<RequestResult<JsonDocument>> PostJSON(string url, string json = null, int timeoutMS = 30000)
        {
            string error = null;
            try
            {
                using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeoutMS)))
                using (var response = await HttpClient.PostAsync(url, new StringContent(!string.IsNullOrEmpty(json) ? json : "", Encoding.UTF8, "application/json"), cts.Token))
                {
                    var success = response.StatusCode == HttpStatusCode.OK;
                    var validationError = response.StatusCode == (HttpStatusCode)422;
                    if (!success && !validationError)
                        return new RequestResult<JsonDocument>(null, $"Unknown Error: {response.StatusCode}", false);

                    using (var contentStream = await response.Content.ReadAsStreamAsync())
                        return new RequestResult<JsonDocument>(await JsonDocument.ParseAsync(contentStream, cancellationToken: cts.Token), validationError ? "Validation Error" : "", true);
                }
            }
            catch (Exception ex)
            {
                error = ex.Message;
            }

            return new RequestResult<JsonDocument>(null, error, false);
        }

        public static async Task<RequestResult<T>> GetJson<T>(string url, int timeoutMS = 30000) where T : class
        {
            string error = null;
            try
            {
                using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeoutMS)))
                using (var response = await HttpClient.GetAsync(url, cts.Token).ConfigureAwait(false))
                {
                    var success = response.StatusCode == HttpStatusCode.OK;
                    var validationError = response.StatusCode == (HttpStatusCode)422;
                    if (!success && !validationError)
                        return new RequestResult<T>(null, $"Unknown Error: {response.StatusCode}", false);

                    var str = response.Content.ReadAsStringAsync();
                    using (var contentStream = await response.Content.ReadAsStreamAsync())
                        return new RequestResult<T>(JsonSerializer.Deserialize<T>(contentStream, jsonSerializerOptions), validationError ? "Validation Error" : "", true);
                }
            }
            catch (Exception ex)
            {
                error = ExceptionUtil.FormatInnerExceptions(ex);
            }

            return new RequestResult<T>(null, error, false);
        }

        public static async Task<RequestResult<JsonDocument>> PostJSONAuth(string url, string oauthToken, string json = null, int timeoutMS = 30000, CancellationToken cancel = default(CancellationToken))
        {
            string error = null;
            try
            {
                using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, url))
                {
                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", oauthToken);
                    requestMessage.Content = new StringContent(!string.IsNullOrEmpty(json) ? json : "", Encoding.UTF8, "application/json");
                    using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeoutMS)))
                    using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancel))
                    using (var response = await HttpClient.SendAsync(requestMessage, linkedCts.Token))
                    {
                        var success = response.StatusCode == HttpStatusCode.OK;
                        var validationError = response.StatusCode == (HttpStatusCode)422;
                        if (!success && !validationError)
                            return new RequestResult<JsonDocument>(null, $"Unknown Error: {response.StatusCode}", false);

                        using (var contentStream = await response.Content.ReadAsStreamAsync())
                            return new RequestResult<JsonDocument>(await JsonDocument.ParseAsync(contentStream, cancellationToken: linkedCts.Token), validationError ? "Validation Error" : "", true);
                    }
                }
            }
            catch (Exception ex)
            {
                error = ex.Message;
            }

            return new RequestResult<JsonDocument>(null, error, false);
        }

        public static async Task<RequestResult<T>> GetJsonAuth<T>(string url, string oauthToken, int timeoutMS = 30000) where T : class
        {
            string error = null;
            try
            {
                using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeoutMS)))
                using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, url))
                {
                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", oauthToken);
                    using (var response = await HttpClient.SendAsync(requestMessage, cts.Token).ConfigureAwait(false))
                    {
                        var success = response.StatusCode == HttpStatusCode.OK;
                        var validationError = response.StatusCode == (HttpStatusCode)422;
                        if (!success && !validationError)
                            return new RequestResult<T>(null, $"Unknown Error: {response.StatusCode}", false);

                        var str = response.Content.ReadAsStringAsync();
                        using (var contentStream = await response.Content.ReadAsStreamAsync())
                            return new RequestResult<T>(JsonSerializer.Deserialize<T>(contentStream, jsonSerializerOptions), validationError ? "Validation Error" : "", true);
                    }
                }


            }
            catch (Exception ex)
            {
                error = ExceptionUtil.FormatInnerExceptions(ex);
            }

            return new RequestResult<T>(null, error, false);
        }
    }
}
